#include "include/defines.h"
#include "include/sdl/pantalla.h"
#include "include/sockets/sockets.h"

// Funciones secundarias
void actualizar_matriz(int);
void iniciar_semaforos();
void iniciar_vars();
void ImprimirTablero(short int [][3]);
void buscar_coordenadas(int,int*,int*);
void cambia_partida();

// Handler
void handler(int);

// Función error
void error(char*);

// Variables globales
t_scr principal;
s_sockets socks;							// Descriptores que entrega server maestro
s_mov mov;										// Variable registra movimientos

// Threads IDs
pthread_t tid_socket;
pthread_t tid_pantalla;
pthread_t tid_lectura;

// Semáforos movimientos
sem_t nuevo_mov;
sem_t sem_mov;
sem_t mov_valido;

// Semáforos threads
sem_t mutex_pantalla;
sem_t mutex_periferico;
sem_t mutex_socket;
sem_t mutex_socket_s;
sem_t socket_s_disp;

// Código Cliente
int main()
{
	int pos_ant=-1;
	
	// Administrar señales
	signal(SIGINT,handler);
	
	printf("Iniciando...\n");
	
	// Iniciar SDL
	start_sdl();
	
	// Inicio todos los semáforos
	iniciar_semaforos();
	
	// Inicializo variables del cliente
	iniciar_vars();
		
	// Iniciar Threads
	pthread_create(&tid_socket, NULL, thread_socket_c, (void*) &mov);
	pthread_create(&tid_pantalla, NULL, thread_pantalla, (void*) &mov);
	pthread_create(&tid_lectura, NULL, thread_lectura, (void*) &mov);

	sleep(1);

	// Desbloqueo pantalla para que refresque
	unlock_pantalla();
	
	// Espero mensaje del socket
	sem_wait(&nuevo_mov);
	
	while(1)
	{
		do
		{
			// Si hubo movimiento del otro jugador
			if(mov.posicion != 0)
			{
				printf("Movimiento anterior: %d -> %d\n",mov.posicion,mov.n_posicion);
				actualizar_matriz(1);
				unlock_pantalla();
			}
			
			// Desbloqueo teclado para que lea
			unlock_periferico();
		
			// Espero que me de un mensaje
			sem_wait(&nuevo_mov);
			printf("Se ingresó un movimiento, tengo que enviarlo\n");
			
			// Desbloqueo a socket para que envíe
			unlock_socket();
			
			// Espero que me entregue un mensaje
			sem_wait(&nuevo_mov);
		}while(sem_trywait(&mov_valido));
		
		printf("Se ingresó un movimiento válido\n");
		
		if(mov.accion == 0)
		{
			pos_ant = mov.posicion;
			
			do
			{
				printf("Selecciono 2da posicion\n");
				// Desbloqueo teclado para que lea
				unlock_periferico();
			
				// Espero que me de un mensaje
				sem_wait(&nuevo_mov);
				
				mov.n_posicion = mov.posicion;
				mov.posicion = pos_ant;
				
				// Desbloqueo a socket para que envíe
				printf("Socket tiene que enviar movimiento: %d -> %d\n",mov.posicion,mov.n_posicion);
				unlock_socket();
				
				// Espero que me entregue un mensaje
				sem_wait(&nuevo_mov);
			}while(sem_trywait(&mov_valido));
			
			mov.posicion = pos_ant;
			printf("Se ingresó un movimiento válido: %d -> %d\n",mov.posicion,mov.n_posicion);
		}
				
		// Decremento validez
		//sem_wait(&mov_valido);
		
		// Uso dato para actualizar matriz
		actualizar_matriz(0);
		ImprimirTablero(principal.matriz);
		
		// Desbloqueo pantalla para que refresque
		unlock_pantalla();
				
		// Espero por nuevo movimiento
		sem_wait(&nuevo_mov);
		
		// Comprobar estado partida
		if(mov.estado != 0)
			cambia_partida();
		
		sleep(1);
	}
	
	// Espero a que terminen
	pthread_join(tid_socket, NULL);
	pthread_join(tid_pantalla, NULL);
	pthread_join(tid_lectura, NULL);
}

void actualizar_matriz(int val)
{
	int posc=0,posf=0,n_posc=0,n_posf=0,aux=-1;
	
	// Transformar coordenadas
	buscar_coordenadas(mov.posicion,&posf,&posc);
	
	// Movimiento nuestro o de rival? val
	// Hubo desplazamiento o colocación?
	if(mov.accion == 2 || mov.n_posicion != 0)
	{
		// Transformar coordenadas
		buscar_coordenadas(mov.n_posicion,&n_posf,&n_posc);
		principal.matriz[posf][posc] = 0;
		posc = n_posc;
		posf = n_posf;
	}
	
	if(val == 0)
		principal.matriz[posf][posc] = ESFERA;
	else
		principal.matriz[posf][posc] = CUBO;
}

void iniciar_semaforos()
{
	// Semáforos
	sem_init(&nuevo_mov,4,0);						// Mutex para inicio partida
	sem_init(&sem_mov,4,1);							// Mutex para mov
	sem_init(&mov_valido,4,0);					// Validez de último movimiento
	
	// Semáforos sincronización threads
	sem_init(&mutex_pantalla,3,0);
	sem_init(&mutex_periferico,3,0);
	sem_init(&mutex_socket,3,0);
}

void iniciar_vars()
{
	int i,j;
		
	for(i=0;i<3;i++)
		for(j=0;j<3;j++)
			principal.matriz[i][j] = 0;
			
	mov.posicion=mov.estado=mov.accion=0;
}

void handler(int sig)
{
	pthread_cancel(tid_socket);
	pthread_cancel(tid_pantalla);
	pthread_cancel(tid_lectura);
	exit(0);
}

void ImprimirTablero(short int mat[][3])
{
	int f,c;
	
	for(f=0;f<3;f++)
	{
		for(c=0;c<3;c++)
			printf("| %d |",mat[f][c]);
					
		printf("\n");
	}
}

void buscar_coordenadas(int posicion, int *fDestino, int *cDestino)
{
	int f,c,pos=1;
	
	for(f=0;f<3;f++)
		for(c=0;c<3;c++)
		{
			if(pos == posicion)
			{
				*fDestino = f;
				*cDestino = c;
				return;
			}
			pos++;
		}
}

void cambia_partida()
{
	// Reinicializo variables
	iniciar_vars();
	iniciar_semaforos();
	sem_post(&sem_mov);
	
	unlock_pantalla();
}

// Funcion error
void error(char *e)
{
	printf("%s\n",e);
	finalizar_sdl();
	exit(1);
}
